package com.onec.service.gateway.service.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.onec.service.api.content.DevelopContent;
import com.onec.service.api.dto.request.RequestData;
import com.onec.service.api.dto.request.customer.AgentInfoReq;
import com.onec.service.api.dto.request.gateway.BizContent;
import com.onec.service.api.dto.response.ResponseData;
import com.onec.service.api.dto.response.customer.AgentInfoResp;
import com.onec.service.api.enums.error.GatewayEnum;
import com.onec.service.api.exception.GatewayException;
import com.onec.service.api.service.customer.AgentInfoApiService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.server.ServerWebExchange;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 对不同请求来源的数据做不同的鉴权处理：
 *
 * @author onec
 * @see com
 * @since 20201119
 */
public class DefaultBaseSourceServiceImpl implements BaseSourceService {

    private static final Logger logger = LoggerFactory.getLogger(DefaultBaseSourceServiceImpl.class);

    @Value("${spring.profiles.active:local}")
    String develop;

    @Autowired
    RedisTemplate redisTemplate;

    @DubboReference(version = "1.0.1", timeout = 6000)
    AgentInfoApiService agentInfoApiService;

    AgentInfoResp getAvailableAgent(String agentNo) {
        AgentInfoReq agentInfoReq = new AgentInfoReq();
        agentInfoReq.setAgentCode(agentNo);
        AgentInfoResp agentInfoResp = agentInfoApiService.getAgent(agentInfoReq);
        if (!agentInfoResp.isSuccess()) {
            throw new GatewayException(agentInfoResp.getCode(), agentInfoResp.getMessage());
        }
        return agentInfoResp;
    }

    /**
     * @param data    需要签名的数据
     * @param signKey 签名key
     * @return 签名值
     */
    String getMd5Sign(String data, String signKey) {
        Map bizMap = JSONUtil.toBean(data, Map.class);
        String requestParam = MapUtil.sortJoin(bizMap, "&", "=", true);
        logger.info("平台md5签名：签名体：【{}】密钥：【{}】", requestParam, signKey);
        return DigestUtil.md5Hex(requestParam + signKey, CharsetUtil.CHARSET_UTF_8).toUpperCase();
    }


    /**
     * 机构密钥解密：
     *
     * @param requestData
     * @return
     */
    BizContent decryptAgentNo(RequestData requestData) {

        AgentInfoResp agentInfoResp = getAvailableAgent(requestData.getAgentNo());
        logger.info("当前请求环境配置：{}", develop);
        if (isSitDevelop()) {
            return BeanUtil.toBean(requestData.getBizContent(), BizContent.class);
        }

        // 使用下游公钥解密：
        RSA rsa = SecureUtil.rsa(null, agentInfoResp.getDecryptKey());
        String data = null;
        try {
            byte[] bizContent = Base64.decode(requestData.getBizContent());
            byte[] decryptData = rsa.decrypt(bizContent, KeyType.PublicKey);
            data = StrUtil.str(decryptData, CharsetUtil.CHARSET_UTF_8);
        } catch (Exception e) {
            e.printStackTrace();

        }
        if (Optional.ofNullable(data).isEmpty()) {
            throw new GatewayException(GatewayEnum.DECRYPT_ERROR);
        }
        String md5Sign = getMd5Sign(data, agentInfoResp.getSignKey());

        logger.info("平台md5:{}", md5Sign);
        logger.info("上送md5:{}", requestData.getSign());
        if (!Objects.equals(md5Sign, requestData.getSign().toUpperCase())) {
            throw new GatewayException(GatewayEnum.SIGN_ERROR);
        }
        return BeanUtil.toBean(data, BizContent.class);
    }


    /**
     * 机构密钥加密：
     *
     * @param responseData
     * @return
     */
    ResponseData encryptAgentNo(ResponseData responseData) {
        logger.info("当前请求返回环境配置：{}", develop);
        if (isSitDevelop()) {
            return responseData;
        }
        AgentInfoResp agentInfoResp = getAvailableAgent(responseData.getAgentNo());
        String bizContent = responseData.getBizContent().toString();

        String md5Sign = getMd5Sign(bizContent, agentInfoResp.getSignKey());
        responseData.setSign(md5Sign);
        // 使用下游公钥解密：
        RSA rsa = SecureUtil.rsa(agentInfoResp.getEncryptKey(), null);

        String data = null;
        try {
            byte[] content = StrUtil.bytes(bizContent, CharsetUtil.CHARSET_UTF_8);
            byte[] encryptData = rsa.encrypt(content, KeyType.PrivateKey);
            data = StrUtil.str(Base64.encode(encryptData), CharsetUtil.CHARSET_UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        responseData.setBizContent(data);
        return responseData;
    }

    @Override
    public BizContent decryptRequestData(RequestData requestData, ServerWebExchange exchange) {
        return null;
    }

    @Override
    public ResponseData encryptResponseData(ResponseData responseData) {
        return null;
    }

    private boolean isSitDevelop() {
        return DevelopContent.SIT.equals(develop);
    }
}
